﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;

using Bandit.Helpers;
using Bandit.UI;

using IOT.DongleFlashTool.ViewModel;
using IOT.DongleFlashTool.Utils;
using IOT.DongleFlashTool.Model;

namespace IOT.DongleFlashTool
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : BanditWindow
    {
        #region 全局变量
        private MainViewModel _MainViewModel = new MainViewModel();
        private bool _TestFinishFlag = false;
        private Process _Process = null;
        private string _CurrentPortName = string.Empty;
        private string _CurrentFlashType = string.Empty;

        private static BanditSerialPort _SerialPort = null;
        private static List<byte> _RecieveBytes = new List<byte>();
        private static AutoResetEvent _AutoResetEvent = new AutoResetEvent(false);
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += MainWindow_Loaded;

            // ListBox加一个按钮，清空整个ListBox TODO 

            // 那个输入Hex文本的，可不可以优化一下体验感~ 限制固定输入几个，然后只能输入特定Hex字符 TODO
        }

        /// <summary>
        /// 窗体加载
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            CBoxSerialPort_DropDownOpened(null, null);
            this.DataContext = this._MainViewModel;
            this._MainViewModel.FirewarePath = ConfigHelper.GetVauleToString("FirewarePath");
            this._MainViewModel.OTAFirewarePath = ConfigHelper.GetVauleToString("OTAFirewarePath");

            // 开始烧录
            this.btnStartFlash.Click += delegate
            {
                btnStartFlash_Click();
            };

            // 选择烧录文件
            this.btnChooseFile.Click += delegate
            {
                btnChooseFile_Click();
            };

            // 选择烧录文件
            this.btnChooseOTAFile.Click += delegate
            {
                btnChooseOTAFile_Click();
            };

            // 烧录OTA文件
            this.btnOtaFlash.Click += delegate
            {
                btnOtaFlash_Click();
            };

            // 开启/关闭 加网
            this.btnToggleJoin.Click += delegate
            {
                btnToggleJoin_Click();
            };

            // 开始OTA
            this.btnStartOTA.Click += delegate
            {
                btnStartOTA_Click();
            };

            // Clear Node
            this.btnClearNode.Click += delegate
            {
                this._MainViewModel.OnlineClients.Clear();
            };

            this.CBoxSerialPort.DropDownOpened += CBoxSerialPort_DropDownOpened;

            this._MainViewModel.CurrentDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            dispatcherTimer.Tick += dispatcherTimer_Tick;
            dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
            dispatcherTimer.Start();

            _SerialPort = new BanditSerialPort();
            _SerialPort.OnReceived += _SerialPort_OnReceived;

            for (int i = 0; i < 16; i++)
            {
                _MainViewModel.OnlineClients.Add(new AAA()
                {
                    Text = "Stupid" + i.ToString(),
                    Icon = i % 2 == 0 ? @"Images\logo-red.png" : @"Images\logo-green.png"
                });
            }
        }

        /// <summary>
        /// 网关串口接收回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _SerialPort_OnReceived(object sender, OnReceivedArgs e)
        {
            _RecieveBytes.AddRange(e.SourceData);

            if (_RecieveBytes.Count > 4 && _RecieveBytes[0] == 0x7E && _RecieveBytes[_RecieveBytes.Count - 1] == 0x7E)
            {
                LogHelper.Info("<<<" + BytesHelper.DataBytesToHexString(_RecieveBytes.ToArray()), true);
                byte[] bytes = BytesHelper.UnEscapeDataBytes(_RecieveBytes.ToArray());  // 反转义

                if (_RecieveBytes[1] == 0xF8)
                {
                    if (BytesHelper.CheckParityByte(bytes))
                    {
                        switch (bytes[2])
                        {
                            case (byte)CommandEnum.None:
                                _AutoResetEvent.Set();
                                break;
                            case (byte)CommandEnum.JoinNetwork:
                                _AutoResetEvent.Set();
                                break;
                            default:
                                break;
                        }
                    }
                }
                else if (_RecieveBytes[1] == 0xF9)
                {
                    if (BytesHelper.CheckParityByte(bytes))
                    {
                        switch (bytes[2])
                        {
                            case (byte)CommandEnum.None:
                                _AutoResetEvent.Set();
                                break;
                            case (byte)CommandEnum.GetMac:
                                GetMac(bytes);
                                break;
                            case (byte)CommandEnum.OTAing:
                                SetOTAStatus(bytes);
                                break;
                            case (byte)CommandEnum.OTAfinished:
                                GetOTAStatus(bytes);
                                break;
                            default:
                                break;
                        }
                    }
                }
                _RecieveBytes.Clear();
            }
        }

        /// <summary>
        /// OTA进行中，显示正在OTA
        /// </summary>
        private void SetOTAStatus(byte[] bytes)
        {
            int dataLen = (int)bytes[4] * 256 + bytes[3];
            byte[] dataBytes = bytes.Skip(5).Take(dataLen).ToArray();

            // MAC地址
            byte[] macBytes = dataBytes.Take(8).ToArray();
            string macStr = BytesHelper.DataBytesToHexString(macBytes, false);

            // 提示对应ListBox标签当前状态,显示正在OTA TODO

            _AutoResetEvent.Set();
        }

        /// <summary>
        /// OTA结束判断
        /// </summary>
        private void GetOTAStatus(byte[] bytes)
        {
            int dataLen = (int)bytes[4] * 256 + bytes[3];
            byte[] dataBytes = bytes.Skip(5).Take(dataLen).ToArray();

            // MAC地址
            byte[] macBytes = dataBytes.Take(8).ToArray();
            string macStr = BytesHelper.DataBytesToHexString(macBytes, false);

            // 提示对应ListBox标签当前状态,显示OTA完毕 TODO

            _AutoResetEvent.Set();
        }

        /// <summary>
        /// 获取Mac地址
        /// </summary>
        private void GetMac(byte[] bytes)
        {
            int dataLen = (int)bytes[4] * 256 + bytes[3];
            byte[] dataBytes = bytes.Skip(5).Take(dataLen).ToArray();

            // MAC地址
            byte[] macBytes = dataBytes.Take(8).ToArray();
            string macStr = BytesHelper.DataBytesToHexString(macBytes, false);
            //if (!this._MainViewModel.OnlineClients.Contains(macStr))
            //{
            //    this._MainViewModel.OnlineClients.Add(macStr);
            //}

            _AutoResetEvent.Set();
        }

        /// <summary>
        /// 显示当前时间
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dispatcherTimer_Tick(object sender, EventArgs e)
        {
            this._MainViewModel.CurrentDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }

        /// <summary>
        /// Dongle串口号选择
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CBoxSerialPort_DropDownOpened(object sender, EventArgs e)
        {
            this.CBoxSerialPort.Items.Clear();
            foreach (string portName in SerialPort.GetPortNames())
            {
                this.CBoxSerialPort.Items.Add(portName);
            }

            if (this.CBoxSerialPort.Items.Count > 0)
            {
                this.CBoxSerialPort.SelectedIndex = this.CBoxSerialPort.Items.Count - 1;
            }
        }

        /// <summary>
        /// 获取烧录文件路径
        /// </summary>
        private void btnChooseFile_Click()
        {
            var openFileDialog = new Microsoft.Win32.OpenFileDialog()
            {
                Filter = "Exe Files (*.bin)|*.bin"
            };
            var result = openFileDialog.ShowDialog();
            if (result == true)
            {
                this._MainViewModel.FirewarePath = openFileDialog.FileName;
                ConfigHelper.SetValue("FirewarePath", this._MainViewModel.FirewarePath);
            }
        }

        /// <summary>
        /// 广播命名开始OTA
        /// </summary>
        private void btnStartOTA_Click()
        {
            // 串口未打开则先打开串口
            if (!_SerialPort.IsOpen)
            {
                _SerialPort.PortName = this.CBoxSerialPort.Text;
                _SerialPort.BaudRate = 115200;
                _SerialPort.Open();
            }

            SendDataBytes sendBytes = new SendDataBytes();
            sendBytes.PackageCommandByte = CommandEnum.ImageNotify;

            byte[] endPoint = strToHexByte(this._MainViewModel.Endpoint);
            byte[] manuCode = strToHexByte(this._MainViewModel.ManuCode);
            byte[] imageType = strToHexByte(this._MainViewModel.ImageType);
            byte[] fileVersion = strToHexByte(this._MainViewModel.FileVersion);

            sendBytes.PackageDataBytes = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, endPoint[0],
                manuCode[0], manuCode[1], imageType[0], imageType[1],
                fileVersion[0], fileVersion[1], fileVersion[2], fileVersion[3] };
            sendBytes.GetWholePackageDataBytes();

            _AutoResetEvent.Reset();
            SendMsgToGateway(sendBytes.WholePackageDataBytes, "开启加网");
            _AutoResetEvent.WaitOne(3000);
            this.btnToggleJoin.Content = "Join Disable";
        }

        /// <summary>
        /// 字符串转16进制字节数组
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        private byte[] strToHexByte(string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
            {
                hexString += " ";
            }

            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
            {
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            }
            return returnBytes;
        }

        /// <summary>
        /// 获取OTA烧录文件路径
        /// </summary>
        private void btnChooseOTAFile_Click()
        {
            var openFileDialog = new Microsoft.Win32.OpenFileDialog()
            {
                Filter = "Exe Files (*.zigbee)|*.zigbee"
            };
            var result = openFileDialog.ShowDialog();
            if (result == true)
            {
                this._MainViewModel.OTAFirewarePath = openFileDialog.FileName;
                ConfigHelper.SetValue("OTAFirewarePath", this._MainViewModel.OTAFirewarePath);
            }
        }

        /// <summary>
        /// 开始烧录
        /// </summary>
        private void btnStartFlash_Click()
        {
            if (_SerialPort.IsOpen)
            {
                _SerialPort.Close();
            }

            if (string.IsNullOrEmpty(this.CBoxSerialPort.Text))
            {
                this.ucPormpt.ShowPrompt("请先选择Dongle串口号！");
                return;
            }

            if (!System.IO.File.Exists(this._MainViewModel.FirewarePath))
            {
                this.ucPormpt.ShowPrompt("请先选择烧录文件路径！");
                return;
            }

            this._MainViewModel.ShowInfo = "Start Dongle Flash......";
            _CurrentPortName = this.CBoxSerialPort.Text;
            System.Threading.Tasks.Task.Factory.StartNew(() =>
                ThreadStartFlashJN51xx());
        }

        /// <summary>
        /// 烧录OTA文件
        /// </summary>
        private void btnOtaFlash_Click()
        {
            if (_SerialPort.IsOpen)
            {
                _SerialPort.Close();
            }

            if (string.IsNullOrEmpty(this.CBoxSerialPort.Text))
            {
                this.ucPormpt.ShowPrompt("请先选择Dongle串口号！");
                return;
            }

            if (!System.IO.File.Exists(this._MainViewModel.OTAFirewarePath))
            {
                this.ucPormpt.ShowPrompt("请先选择OTA烧录文件路径！");
                return;
            }

            this._MainViewModel.ShowInfo = "Start OTA Flash......";
            _CurrentPortName = this.CBoxSerialPort.Text;
            System.Threading.Tasks.Task.Factory.StartNew(() =>
                ThreadStartFlashJN51xxOTA());
        }

        /// <summary>
        /// 开始/关闭 加网
        /// </summary>
        private void btnToggleJoin_Click()
        {
            // 串口未打开则先打开串口
            if (!_SerialPort.IsOpen)
            {
                _SerialPort.PortName = this.CBoxSerialPort.Text;
                _SerialPort.BaudRate = 115200;
                _SerialPort.Open();
            }

            if (this.btnToggleJoin.Content.ToString() == "Join Enable")
            {
                SendDataBytes sendBytes = new SendDataBytes();
                sendBytes.PackageCommandByte = CommandEnum.JoinNetwork;
                sendBytes.PackageDataBytes = new byte[] { 0x3C, 0x00, 0x00, 0x00, 0x00 };
                sendBytes.GetWholePackageDataBytes();

                _AutoResetEvent.Reset();
                SendMsgToGateway(sendBytes.WholePackageDataBytes, "开启加网");
                if (!_AutoResetEvent.WaitOne(3000)) 
                {
                    _SerialPort = null;
                    _SerialPort = new BanditSerialPort();
                    _SerialPort.OnReceived += _SerialPort_OnReceived;
                }
                this.btnToggleJoin.Content = "Join Disable";
            }
            else
            {
                SendDataBytes sendBytes = new SendDataBytes();
                sendBytes.PackageCommandByte = CommandEnum.JoinNetwork;
                sendBytes.PackageDataBytes = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00 };
                sendBytes.GetWholePackageDataBytes();

                _AutoResetEvent.Reset();
                SendMsgToGateway(sendBytes.WholePackageDataBytes, "关闭加网");
                _AutoResetEvent.WaitOne(3000);
                if (!_AutoResetEvent.WaitOne(3000))
                {
                    _SerialPort = null;
                    _SerialPort = new BanditSerialPort();
                    _SerialPort.OnReceived += _SerialPort_OnReceived;
                }
                this.btnToggleJoin.Content = "Join Enable";
            }
        }

        /// <summary>
        /// 发数据给网关
        /// </summary>
        /// <param name="bytes"></param>
        private void SendMsgToGateway(byte[] bytes, string remark = "")
        {
            if (_SerialPort.IsOpen)
            {
                _RecieveBytes.Clear();

                LogHelper.Info(remark);
                LogHelper.Info(">>>" + BytesHelper.DataBytesToHexString(bytes), true);
                _SerialPort.Send(BytesHelper.EscapeDataBytes(bytes));  // 发送前转义
            }
        }

        /// <summary>
        /// 线程烧录
        /// </summary>
        /// <param name="portName"></param>
        private void ThreadStartFlashJN51xx()
        {
            _TestFinishFlag = false;
            _CurrentFlashType = "Dongle";
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    this._MainViewModel.Icon = "Images/logo-blue.png";

                    Thread.Sleep(222);

                    this._MainViewModel.Icon = "Images/logo_blue_pink.png";

                    Thread.Sleep(222);

                    if (_TestFinishFlag)
                    {
                        break;
                    }
                }
            });

            string cmdStr2 = System.IO.Path.Combine(GetAssemblyPath(), "JN51xxProgrammer/JN51xxProgrammer.exe");
            string cmdStr3 = string.Format("{0} -s {1} -V 2 -f {2} -v --eraseeeprom=full",
                "\"" + cmdStr2 + "\"", _CurrentPortName, "\"" + this._MainViewModel.FirewarePath + "\"");

            RunCmdCommand(cmdStr3);
        }

        /// <summary>
        /// 线程烧录OTA文件
        /// </summary>
        private void ThreadStartFlashJN51xxOTA()
        {
            _TestFinishFlag = false;
            _CurrentFlashType = "OTA";
            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    this._MainViewModel.Icon = "Images/logo-blue.png";

                    Thread.Sleep(222);

                    this._MainViewModel.Icon = "Images/logo_blue_pink.png";

                    Thread.Sleep(222);

                    if (_TestFinishFlag)
                    {
                        break;
                    }
                }
            });

            string cmdStr2 = System.IO.Path.Combine(GetAssemblyPath(), "JN51xxProgrammer/JN51xxProgrammer.exe");
            string cmdStr3 = string.Format("{0} -S external -V 2 -s {1} -f {2} -Y",
                "\"" + cmdStr2 + "\"", _CurrentPortName, "\"" + this._MainViewModel.OTAFirewarePath + "\"");

            RunCmdCommand(cmdStr3);
        }

        /// <summary>
        /// 执行cmd命令
        /// </summary>
        /// <param name="cmd"></param>
        public void RunCmdCommand(string cmd)
        {
            _Process = new Process();
            _Process.StartInfo.FileName = @"C:\Windows\System32\cmd.exe";
            _Process.StartInfo.UseShellExecute = false;        //是否使用操作系统shell启动
            _Process.StartInfo.RedirectStandardInput = true;   //接受来自调用程序的输入信息
            _Process.StartInfo.RedirectStandardOutput = true;  //由调用程序获取输出信息
            _Process.StartInfo.RedirectStandardError = true;   //重定向标准错误输出
            _Process.StartInfo.CreateNoWindow = true;          //不显示程序窗口
            _Process.OutputDataReceived += _Process_OutputDataReceived;
            _Process.Start();//启动程序

            //向cmd窗口写入命令
            _Process.StandardInput.WriteLine(cmd);
            _Process.BeginOutputReadLine();
        }

        /// <summary>
        /// 实时输出CMD指令
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.Data))
            {
                LogHelper.Info(e.Data, false);
                string[] dataList = e.Data.Split(":");
                if (dataList.Length > 1 && dataList[0].Trim() == _CurrentPortName)
                {
                    if (IsNum(dataList[1].Trim()))  // 烧录进度
                    {
                        this._MainViewModel.ProgressValue = Convert.ToInt32(dataList[1].Trim());
                    }
                    else if (dataList[1].Trim().ToLower().Contains("error"))  // 烧录错误
                    {
                        _Process.Close();
                        _TestFinishFlag = true;
                        Thread.Sleep(666);
                        this._MainViewModel.ShowInfo = e.Data;
                        this._MainViewModel.ProgressValue = 100;
                        this._MainViewModel.Icon = @"Images\logo-red.png";
                    }
                    else if (dataList[1].Trim().ToLower().Contains("eeprom erased successfully"))  //烧录完成
                    {
                        _TestFinishFlag = true;
                        Thread.Sleep(666);
                        this._MainViewModel.ShowInfo = dataList[1].Trim();
                        this._MainViewModel.Icon = @"Images\logo-green.png";
                    }
                    else if (dataList[1].Trim().ToLower().Contains("flash programmed successfully"))  //烧录完成
                    {
                        if (_CurrentFlashType == "OTA")
                        {
                            _TestFinishFlag = true;
                            Thread.Sleep(666);
                            this._MainViewModel.ShowInfo = dataList[1].Trim();
                            this._MainViewModel.Icon = @"Images\logo-green.png";
                        }
                    }
                    else if (dataList[1].Trim().ToLower().Contains("mac address"))  // 获取MAC地址
                    {
                        try
                        {
                            string[] strList = e.Data.Trim().ToLower().Split(" ");
                            this._MainViewModel.Mac = strList[strList.Length - 1].Replace(":", "").ToUpper();
                        }
                        catch
                        {
                            this._MainViewModel.Mac = string.Empty;
                        }
                    }
                    else  // 其他信息显示
                    {
                        this._MainViewModel.ShowInfo = dataList[1].Trim();
                    }
                }
            }
        }

        /// <summary>
        /// 判断是否为纯数字
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private bool IsNum(string str)
        {
            for (int i = 0; i < str.Length; i++)
            {
                if (!Char.IsNumber(str, i))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>  
        /// 获取Assembly的运行路径  
        /// </summary>  
        ///<returns></returns>  
        private string GetAssemblyPath()
        {
            string _CodeBase = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;

            _CodeBase = _CodeBase.Substring(8, _CodeBase.Length - 8);    // 8是file:// 的长度  

            string[] arrSection = _CodeBase.Split(new char[] { '/' });

            string _FolderPath = "";
            for (int i = 0; i < arrSection.Length - 1; i++)
            {
                _FolderPath += arrSection[i] + "/";
            }

            return _FolderPath;
        }
    }
}
